<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Prop</title>
    <script src="../vue.js"></script>
  </head>
  <body>
    <!-- 1 Prop 的大小写 -->
    <div id="app1">
      <!-- 在 HTML 中是 kebab-case 的 -->
      <blog-post post-title="title1"></blog-post>
    </div>
    <hr />
    <!-- 2 Prop 类型 -->
    <hr />
    <!-- 3 传递静态或动态内容给 Prop -->
    <!-- 传入一个静态的值 -->
    <div id="app2">
      <blog-post post-title="My journey with Vue"></blog-post>
      <hr />
    </div>
    <!-- 动态赋予一个变量的值 -->
    <div id="app3">
      <blog-post v-bind:post-title="post.title"></blog-post>
    </div>
    <hr />
    <!-- 动态赋予一个复杂表达式的值 -->
    <div id="app4">
      <blog-post v-bind:post-title="post.title + ' by ' + post.author"></blog-post>
    </div>
    <hr />
    <!-- 3.1 传入一个数字 -->
    <!-- 即便 `42` 是静态的，我们仍然需要 `v-bind` 来告诉 Vue 这是一个 JavaScript 表达式而不是一个字符串。 -->
    <div id="app5">
      <blog-post v-bind:likes="42"></blog-post>
    </div>
    <hr />
    <!-- 用一个变量进行动态赋值。-->
    <div id="app6">
      <blog-post v-bind:likes="post.likes"></blog-post>
    </div>
    <hr />
    <!-- 3.2 传入一个布尔值 -->
    <!-- 包含该 prop 没有值的情况在内，都意味着 `true`。-->
    <div id="app7">
      <blog-post is-published></blog-post>
    </div>
    <hr />
    <div id="app8">
      <!-- 即便 `false` 是静态的，我们仍然需要 `v-bind` 来告诉 Vue 这是一个 JavaScript 表达式而不是一个字符串 -->
      <blog-post v-bind:is-published='false'></blog-post>
    </div>
    <hr />
    <div id="app9">
      <!-- 用一个变量进行动态赋值 -->
      <blog-post v-bind:is-published='post.isPublished'></blog-post>
    </div>
    <hr />
    <!-- 3.3 传入一个数组 -->
    <!-- 即便数组是静态的，我们仍然需要 `v-bind` 来告诉 Vue，这是一个 JavaScript 表达式而不是一个字符串 -->
    <div id="app10">
      <blog-post v-bind:comment-ids='["a01", "a02", 273]'></blog-post>
    </div>
    <hr />
    <div id="app11">
      <!-- 用一个变量进行动态赋值 -->
      <blog-post v-bind:comment-ids='post.commentIds'></blog-post>
    </div>
    <hr />
    <!-- 3.4 传入一个对象 -->
    <div id="app12">
      <!-- 即便对象是静态的，我们仍然需要 `v-bind` 来告诉 Vue 这是一个 JavaScript 表达式而不是一个字符串 -->
      <blog-post v-bind:author='{firstName: "witt", lastName: "zhang"}'></blog-post>
    </div>
    <hr />
    <div id="app13">
      <!-- 用一个变量进行动态赋值 -->
      <blog-post v-bind:author='post.author'></blog-post>
    </div>
    <hr />
    <!-- 3.5 传入一个对象的所有 property -->
    <div id="app14">
      <blog-post v-bind='post'></blog-post>
    </div>
    <hr />
    <!-- 4 单向数据流 -->
    <hr />
    <!-- 5 Prop 验证 -->
    <hr />
    <!-- 5.1 类型检查 -->
    <hr />
    <!-- 6 非 Prop 的 Attribute -->
    <!-- 例如，想象一下你通过一个 Bootstrap 插件使用了一个第三方的 <bootstrap-date-input> 组件，
		这个插件需要在其 <input> 上用到一个 data-date-picker attribute。
		我们可以将这个 attribute 添加到你的组件实例上： -->
    <bootstrap-date-input data-date-picker="activated"></bootstrap-date-input>
    <!-- 然后这个 data-date-picker="activated" attribute 就会自动添加到 <bootstrap-date-input> 的根元素上 -->
    <hr />
    <!-- 6.1 替换/合并已有的 Attribute -->
    <!-- 想象一下 <bootstrap-date-input> 的模板是这样的：
		 <input type="date" class="form-control" />
		 
		 为了给我们的日期选择器插件定制一个主题，我们可能需要像这样添加一个特别的类名：
		 -->
    <bootstrap-date-input data-date-picker='activated' class='date-picker-theme-dark'></bootstrap-date-input>
    <!-- 
		在这种情况下，我们定义了两个不同的 class 的值：
			form-control，这是在组件的模板内设置好的
			date-picker-theme-dark，这是从组件的父级传入的
		对于绝大多数 attribute 来说，从外部提供给组件的值会替换掉组件内部设置好的值。所以如果传入 type="text" 就会替换掉 type="date" 并把它破坏！庆幸的是，class 和 style attribute 会稍微智能一些，即两边的值会被合并起来，从而得到最终的值：form-control date-picker-theme-dark。 
		 -->
    <hr />
    <!-- 6.2 禁用 Attribute 继承 -->
    <!-- 这个模式允许你在使用基础组件的时候更像是使用原始的 HTML 元素，而不会担心哪个元素是真正的根元素： -->
    <div id="app15">
      <cmt6 label="Username" v-model="username" required placeholder="Enter your username"></cmt6>
    </div>
    <hr />
    <script type="text/javascript">
      // 1 Prop 的大小写
      Vue.component('blog-post', {
        // 在 JavaScript 中是 camelCase 的
        props: ['postTitle', 'likes', 'isPublished', 'commentIds', 'author'],
        template: `
				<div>
					<strong>postTitle: {{postTitle}}</strong><br>
					<span>likes: {{likes}}</span><br>
					<span>isPublished: {{isPublished}}</span><br>
					<span>commentIds: {{commentIds}}</span><br>
					<span>author: {{author}}</span>
				</div>
				`
      })
      new Vue({
        el: '#app1'
      })
      // 2 Prop 类型
      //   通常你希望每个 prop 都有指定的值类型。这时，你可以以对象形式列出 prop，这些 property 的名称和值分别是 prop 各自的名称和类型：
      Vue.component('test', {
        props: {
          title: String,
          likes: Number,
          isPublished: Boolean,
          commentIds: Array,
          author: Object,
          callback: Function,
          contactsPromise: Promise // or any other constructor
        }
      })
      // 3 传递静态或动态 Prop
      new Vue({
        el: '#app2',
        data: {
          post: {
            title: 'title2'
          }
        }
      })
      new Vue({
        el: '#app3',
        data: {
          post: {
            title: 'title3'
          }
        }
      })
      new Vue({
        el: '#app4',
        data: {
          post: {
            title: 'title4',
            author: 'witt'
          }
        }
      })
      // 3.1 传入一个数字
      new Vue({
        el: '#app5',
      })
      new Vue({
        el: '#app6',
        data: {
          post: {
            likes: 3
          }
        }
      })
      // 3.2 传入一个布尔值
      new Vue({
        el: '#app7'
      })
      new Vue({
        el: '#app8'
      })
      new Vue({
        el: '#app9',
        data: {
          post: {
            isPublished: true
          }
        }
      })
      // 3.3 传入一个数组
      new Vue({
        el: '#app10'
      })
      new Vue({
        el: '#app11',
        data: {
          post: {
            commentIds: ["c01", 125, "d03"]
          }
        }
      })
      // 3.4 传入一个对象
      new Vue({
        el: '#app12'
      })
      new Vue({
        el: '#app13',
        data: {
          post: {
            author: {
              firstName: "witt2",
              lastName: "zhang2"
            }
          }
        }
      })
      // 3.5 传入一个对象的所有 property
      new Vue({
        el: '#app14',
        data: {
          post: {
            likes: 10,
            isPublished: true,
            commentIds: ['aaa', 'bbb', 'ccc'],
            author: {
              name: 'witt zhang',
              company: 'beyond'
            }
          }
        }
      })
      // 4 单向数据流
      //   这里有两种常见的试图变更一个 prop 的情形：
      Vue.component('cmt1', {
        props: ['initialCounter'],
        //   情形1 这个 prop 用来传递一个初始值；这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下，最好定义一个本地的 data property 并将这个 prop 用作其初始值：
        data: function() {
          return {
            counter: this.initialCounter
          }
        }
      })
      //   情形2 这个 prop 以一种原始的值传入且需要进行转换。在这种情况下，最好使用这个 prop 的值来定义一个计算属性：
      Vue.component('cmt2', {
        props: ['size'],
        computed: {
          normalizedSize: function() {
            return this.size.trim().toLowerCase()
          }
        }
      })

      // 5 Prop 验证（当 prop 验证失败的时候，(开发环境构建版本的) Vue 将会产生一个控制台的警告）
      //   注意那些 prop 会在一个组件实例创建之前进行验证，所以实例的 property (如 data、computed 等) 在 default 或 validator 函数中是不可用的
      Vue.component('cmt3', {
        props: {
          // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
          propA: Number,
          // 多个可能的类型
          propB: [String, Number],
          // 必填的字符串
          propC: {
            type: String,
            required: true
          },
          // 带有默认值的数字
          propD: {
            type: Number,
            default: 100
          },
          // 带有默认值的对象
          propE: {
            type: Object,
            default: function() {
              return {
                message: 'hello'
              }
            }
          },
          // 自定义验证函数
          propF: {
            validator: function(value) {
              // 这个值必须匹配下列字符串中的一个
              return ['success', 'warning', 'danger'].includes(value)
            }
          }
        }
      })
      // 5.1 类型检查
      /* 
       type 可以是下列原生构造函数中的一个：
       
       String
       Number
       Boolean
       Array
       Object
       Date
       Function
       Symbol
       额外的，type 还可以是一个自定义的构造函数，并且通过 instanceof 来进行检查确认。例如，给定下列现成的构造函数：
       */
      function Person(firstName, lastName) {
        this.firstName = firstName
        this.lastName = lastName
      }
      // 可以使用如下方式来验证 author prop 的值是否是通过 new Person 创建的：
      Vue.component('cmt4', {
        props: {
          author: Person
        }
      })

      // 6 非 Prop 的 Attribute
      // 6.1 替换/合并已有的 Attribute
      // 6.2 禁用 Attribute 继承
      //   如果你不希望组件的根元素继承 attribute，你可以在组件的选项中设置 inheritAttrs: false。例如：
      Vue.component('cmt5', {
        inheritAttrs: false,
        // ...
      })
      /* 
      这尤其适合配合实例的 $attrs property 使用，该 property 包含了传递给一个组件的 attribute 名和 attribute 值，例如：
      {
        required: true,
        placeholder: 'Enter your username'
      } 
      
      有了 inheritAttrs: false 和 $attrs，你就可以手动决定这些 attribute 会被赋予哪个元素。在撰写基础组件的时候是常会用到的：
      */
      Vue.component('cmt6', {
        inheritAttrs: false,
        props: ['label', 'value'],
        template: `
					<label>
						{{label}}
						<input v-bind="$attrs"
							v-bind:value="value"
							v-on:input="$emit('input', '$event.target.value')"
						>
					</label>
			   `
      })
      // 注意 inheritAttrs: false 选项不会影响 style 和 class 的绑定

      new Vue({
        el: '#app15',
        data: {
          required: true,
          placeholder: 'Enter your username',
          username: ''
        }
      })
    </script>
  </body>
</html>
